/**
  ETFDAQ Project
  \class TACard
  \brief A VME card for specific data processing and acquiring functions
  Supposed to be a virtual class
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2024-08-12
  \date 2024-08-12 last modified

  \copyright Copyright (c) 2024 IMP-CAS with LGPLv3 LICENSE
*/

#include <iostream>
#include <climits>
#include "TACard.h"
#include "TAController.h"
#include "TACrate.h"
#include "TAMath.h"
#include "TAMsg.h"

using std::cout;
using std::endl;

#define er TAMsg::Error

TACard::TACard(TAController *ctrl, const TAYaml &c) : fConfig(c), fSlot(-1),
fCrate(0), fBaseAddress(-1), fController(ctrl), fNCh(32), fIped(0),
fIsSmallThreshold(0), fIsEmptyEnable(0), fIsKeepUnderflow(0),
fIsKeepOverflow(0), fIRQLevel(0), fIRQVector(0), fEventTrigger(1),
fEventCounter(0), fDNEv(0), fCardType(-1), fSerialNo(-1), fHardWareRev(-1),
fVersion(-1), fFirmWareRev(-1), fDebug(0), fMAX_EVCTR(0xffffff - 100),
fCardPurpose(ECardPurpose::UDF), fThreAddrIncre(2), fChIdBit0(16),
fChIdMask(0x1F){}

TACard::~TACard(){}

// config accroding to user yaml input
void TACard::Config(){
  const auto &c = fConfig; // just for convenience
  if(!fController) er("TACard", "Config: controller is null");
  if(fNCh <= 0) er("TACard", "Config: illegal fNCh: %d", fNCh);

  fCardName = c["type"].as<string>();
  TAMath::uppercase(fCardName);
  fBaseAddress = c["baseaddr"].as<uint32_t>();
  fSlot = c["slot"].as<int>();
  if(fSlot <= 1 || fSlot > TACrate::NSLOT)
    er("TACard", "Config: illegal slot=%d", fSlot);
  if(c["crate"]) fCrate = c["crate"].as<int>();

  if(c["threshold"]){
    for(const auto &t : c["threshold"]) fThres.push_back(t.as<uint16_t>());
    if(fThres.size() != fNCh)
      er("TACard", "Config: nof(%lu) thresholds is not equal to nof channels(%lu)",
        fThres.size(), fNCh);
  } // end if
  if(c["channelOn"]){
    for(const auto &t : c["channelOn"]) fChOn.push_back(bool(t.as<int>()));
    if(fChOn.size() != fNCh)
      er("TACard", "Config: nof(%lu) channelOn is not equal to nof channels(%lu)",
        fChOn.size(), fNCh);
  } // end if
  if(c["iPed"]) fIped = c["iPed"].as<int>();
  if(c["smallThreshold"]) fIsSmallThreshold = c["smallThreshold"].as<bool>();
  if(c["enableEmpty"]) fIsEmptyEnable = c["enableEmpty"].as<bool>();
  if(c["irq"]){
    const TAYaml &i = c["irq"];
    fIRQLevel = i[0].as<int>();
    if(fIRQLevel < 0 || fIRQLevel > 7)
      er("TACard", "Config: illegal IRQ level (%d)", fIRQLevel);
    fIRQVector = i[1].as<int>();
    fEventTrigger = i[2].as<int>();
  } // end if
  if(c["keepUnderflow"]) fIsKeepUnderflow = c["keepUnderflow"].as<bool>();
  if(c["keepOverflow"]) fIsKeepOverflow = c["keepOverflow"].as<bool>();

  fConfig = c;
} // end member function Config

// level 1~7, 0 will disable the IRQ generation
// vector 0x00~0xFF, requested by vme ctrler to identify the interrupter
// nEv: trigger will be generated after nof buffered events reaches nEv
void TACard::ConfigIRQ(int level, int vector, int nEv) const{
  er("TACard", "ConfigIRQ:"
      "%s in slot %d, supposed to be defined in sub-classes", fCardName.data(), fSlot);
} // end member function ConfigIRQ

/// read a uint32_t from the register via A32 addr bus
uint32_t TACard::RegReadA32D32(uint16_t addr) const{
  return fController->A32D32Read(fBaseAddress + addr);
} // end member function RegReadA32D32
/// read a uint16_t from the register via A32 addr bus
uint16_t TACard::RegReadA32D16(uint16_t addr) const{
  return fController->A32D16Read(fBaseAddress + addr);
} // end member function RegReadA32D16
// read a block of uint32_t from the register via A32 addr bus, return n ev read
int TACard::RegReadA32BLT32(uint16_t addr, uint32_t *data, uint32_t nreq) const{
  return fController->A32BLT32Read(fBaseAddress + addr, data, nreq);
} // end member function RegReadA32BLT32
// read a block of uint32_t from the register via A32 addr bus, ret nget blk
int TACard::RegReadA32MBLT64(uint16_t addr, uint32_t *data, uint32_t nreq) const{
  return fController->A32MBLT64Read(fBaseAddress + addr, data, nreq);
} // end member function RegReadA32BLT32

// write a uint32_t to the register via A32 addr bus
void TACard::RegWriteA32D32(uint16_t addr, uint32_t data) const{
  fController->A32D32Write(fBaseAddress + addr, data);
} // end member function RegWriteA32D32
// write a uint16_t to the register via A32 addr bus
void TACard::RegWriteA32D16(uint16_t addr, uint16_t data) const{
  fController->A32D16Write(fBaseAddress + addr, data);
} // end member function RegWriteA32D16

void TACard::Print() const{
  printf("\n\033[36;1m------ slot %d: %s ------\033[0m\n", fSlot, fCardName.data());
  printf("card type: %d, baseaddr: 0x%08x\n", fCardType, fBaseAddress);
  printf("serialNo: %d, hardware version: %d\nfirmware version: %d, version: %d\n",
    fSerialNo, fHardWareRev, fFirmWareRev, fVersion);
  cout << std::boolalpha;
  cout << "Small Threshold: " << fIsSmallThreshold;
  cout << ",\t Empty Enable: " << fIsEmptyEnable << endl;
  cout << "Keep Underflow: " << fIsKeepUnderflow;
  cout << ",\t Keep Overflow: " << fIsKeepOverflow << endl;
  if(fIRQLevel){
    cout << "\033[1mIRQ Level: " << fIRQLevel << ",\t IRQ Vector: ";
    cout << std::hex << fIRQVector << std::dec;
    cout << ",\t Event Trigger: " << fEventTrigger << "\033[0m" << endl;
  } // end if
  if(fIped) cout << "IPed: " << fIped << endl;
  cout << std::noboolalpha;
} // end member function Print

void TACard::TellYouWhoIAm() const{
  printf("------ This is %s in slot %d with baseaddr 0x%08x -------\n",
    fCardName.data(), fSlot, fBaseAddress);
} // end member function TellYouWhoIAm



// clears the event counter
void TACard::ClearEventCounter(){
  er("TACard", "ClearEventCounter: "
    "%s in slot %d, supposed to be defined in sub-classes", fCardName.data(), fSlot);
} // end member function ClearEventCounter

void TACard::EnableEmpty() const{
  er("TACard", "EnableEmpty:"
      "%s in slot %d, supposed to be defined in sub-classes", fCardName.data(), fSlot);
} // end member function EnableEmpty

void TACard::EnableBerr() const{
  er("TACard", "EnableBerr:"
      "%s in slot %d, supposed to be defined in sub-classes", fCardName.data(), fSlot);
} // end member function EnableBerr

void TACard::SetChOn(int ch, bool isOn) const{
  er("TACard", "SetChOn:"
      "%s in slot %d, supposed to be defined in sub-classes", fCardName.data(), fSlot);
} // end member function SetChOn

void TACard::SetThreshold(int ch, uint16_t thre) const{
  er("TACard", "SetThreshold:"
      "%s in slot %d, supposed to be defined in sub-classes", fCardName.data(), fSlot);
} // end member function SetThreshold

void TACard::SetIped(int iped) const{
  er("TACard", "SetIped:"
      "%s in slot %d, supposed to be defined in sub-classes", fCardName.data(), fSlot);
} // end member function SetIped

void TACard::UsingSmallThre(bool opt) const{
  er("TACard", "UsingSmallThre:"
    "%s in slot %d, supposed to be defined in sub-classes", fCardName.data(), fSlot);
} // end member function UsingSmallThre

void TACard::KeepUnderflow(bool opt) const{
  er("TACard", "KeepUnderflow:"
    "%s in slot %d, supposed to be defined in sub-classes", fCardName.data(), fSlot);
} // end member function KeepUnderflow

void TACard::KeepOverflow(bool opt) const{
  er("TACard", "KeepOverflow:"
    "%s in slot %d, supposed to be defined in sub-classes", fCardName.data(), fSlot);
} // end member function KeepOverflow

void TACard::SetReadOneEvent() const{
  er("TACard", "SetReadOneEvent:"
      "%s in slot %d, supposed to be defined in sub-classes", fCardName.data(), fSlot);
} // end member function SetReadOneEvent

void TACard::SetAllTrigger(bool opt) const{
  er("TACard", "SetAllTrigger:"
      "%s in slot %d, supposed to be defined in sub-classes", fCardName.data(), fSlot);
} // end member function SetAllTrigger

void TACard::ReadEventCounter(){
  er("TACard", "ReadEventCounter:"
      "%s in slot %d, supposed to be defined in sub-classes", fCardName.data(), fSlot);
} // end member function ReadEventCounter
